Predict Power from VO2 with RER and GE

Author

Jem Arnold

Published

January 8, 2026

Thread posted

Common question I hear: "My VO2max is x and my current threshold power is y. Is it possible for me to reach an FTP of z?”

It's surprisingly simple to predict power from VO2, and vice versa

We just need reasonable estimates for RER substrate oxidation and metabolic efficiency 📊🧵/4 #rstats #datavis

[image or embed]

— Jem Arnold (@jemarnold.bsky.social) 8 January 2026 at 14:11
Show setup code
library(tidyverse)
library(geomtextpath)
library(ggtext)
library(colorspace)
library(sysfonts)
library(showtext)

## use font & icons in ggplots
sysfonts::font_add_google(
    name = "Merriweather Sans", 
    family = "Merriweather Sans"
)
sysfonts::font_add(
    family = "fa-brands", 
    regular = r"(C:\Users\Jem\AppData\Local\Microsoft\Windows\Fonts\Font Awesome 7 Brands-Regular-400.otf)"
)
showtext::showtext_opts(dpi = 600)
showtext::showtext_auto()

## custom theme
theme_set(
    theme_bw(base_size = 12, base_family = "Merriweather Sans") +
        theme(
            plot.title = ggtext::element_textbox_simple(
                size = rel(1.2), lineheight = 1.1
            ),
            plot.subtitle = ggtext::element_textbox(
                colour = "grey35", face = "italic", hjust = 0, 
                lineheight = 1.1, margin = margin(b = 4)
            ),
            plot.caption = ggtext::element_textbox(
                colour = "grey35", halign = 1
            ),
            panel.border = element_rect(linewidth = 0.8),
            axis.title = element_text(face = "bold"),
            panel.grid.major = element_blank(),
            panel.grid.minor = element_blank(),
            legend.position = "none"
        )
)

social_caption <- "<span style='font-family:fa-brands'>&#xf09b; &#xe671;</span> jemarnold"

Post 1

Common question I hear: “My VO2max is x and my current threshold power is y. Is it possible for me to reach an FTP of z?

It’s surprisingly simple to predict power from VO2, and vice versa

We just need reasonable estimates for RER substrate oxidation and metabolic efficiency 📊🧵/4 #rstats #datavis

Show figure 1 code
VO2max_rel <- 71 ## ml/kg/min
weight <- 71 ## kg
VO2max <- VO2max_rel / 1000 * weight ## ≈ 5.0 L/min
power <- seq(300, 400, by = 10) ## W
RER <- seq(0.85, 0.95, 0.02) ## RER at threshold
GE <- (0.04824536 * log(power) - 0.05657161) + 0.025 ## % derived from Ettema & Lorås, 2009
GE <- round(seq(min(GE) - 0.025, max(GE) + 0.025, 0.01), 2)
GE <- seq(0.2, 0.25, 0.01) ## use pretty GE range anyway

df <- expand_grid(power, RER, GE) |>
    mutate(
        O2kJ = 4.83535 * RER + 16.88348, ## kJ/L derived from Péronnet & Massicotte, 1991
        VO2 = (power * 60 / 1000) / (GE * O2kJ), ## L/min
        VO2_rel = VO2 / VO2max, ## %
    )

ggplot(df, aes(x = power, y = VO2_rel, group = interaction(RER, GE), colour = factor(GE))) +
    labs( ## labs use markdown formatting with {ggtext}
        title = "Is it possible to achieve **380+ W** FTP at **5.0 L/min** VO<sub>2</sub>max?",
        subtitle = "Physiologically yes, with elite efficiency & fractional threshold.",
        caption = str_glue(
            "**Data**: derived from stochiometric equations | **Visuals**: {social_caption}"
        )
    ) +
    coord_cartesian(ylim = c(NA, 1)) +
    scale_x_continuous(
        name = "Functional Threshold Power (W)",
        breaks = power,
        expand = expansion(0.01)
    ) +
    scale_y_continuous(
        name = expression(bold(
            Percent~of~VO['2']*max~'@ 5.0'~L*'·'*min^'-1'~'(%)'
        )),
        label = scales::percent_format(suffix = ""),
        n.breaks = 6,
        expand = expansion(c(0.01, 0))
    ) +
    geom_hline(yintercept = 0.9, linetype = "dashed", colour = "grey60") +
    geom_vline(xintercept = 380, linetype = "dotted", colour = "grey60") +
    geom_line() +
    geomtextpath::geom_textline(
        data = ~ filter(.x, GE == rev(GE)[1L] & RER == 0.85),
        aes(
            label = str_glue("{round(GE*100)}% Gross Metabolic Efficiency"),
            colour = stage(factor(GE), after_scale = colorspace::darken(colour, 0.2))
        ),
        linewidth = 0, size = 4, hjust = 0.15, halign = "left", vjust = 0.2
    ) +
    geomtextpath::geom_textline(
        data = ~ filter(.x, GE != rev(GE)[1L] & RER == 0.85),
        aes(
            label = str_glue("{round(GE*100)}%"),
            colour = stage(factor(GE), after_scale = colorspace::darken(colour, 0.2))
        ),
        linewidth = 0, size = 4, hjust = 0.1, halign = "left", vjust = 0.1
    ) +
    geomtextpath::geom_textline(
        data = ~ filter(.x, GE == rev(GE)[1L] & RER == 0.95),
        aes(
            label = "(colour bands represent RER range = 0.85 - 0.95)",
            colour = stage(factor(GE), after_scale = colorspace::darken(colour, 0.15))
        ),
        linewidth = 0, size = 3.0, hjust = 0.15, halign = "left", vjust = 1.1
    )

Figure visualising the percent of a VO2max of 5.0 L/min required to produce functional threshold power (FTP) between 300 and 400 W, with isolines for gross metabolic efficiency (GE, %) from 20-25% and RER at each GE value between 0.85-0.95. The question was “is it possible to achieve an FTP of 380 W at a VO2max of 5.0 L/min (71 ml/kg/min at 71kg bodyweight)?” Based on these calculations, that would require somewhere around 24% GE and ~90% VO2max, which is just about physiologically plausible for an elite male cyclist… but would certainly take some work to achieve.

Post 2

Reasonable estimates for gross metabolic efficiency (GE) for trained cyclists is 15-25%

GE logarithmically increases with power output, so higher fitness = higher power = higher GE

Prediction model: GE = 0.0482 * log(power) - 0.0566

90% prediction interval for individual values is around ± 2.5%

Figure plotting individually observed gross efficiency (GE) values for cyclists across power output from 50 to 450 W, taken from published cross-sectional data in Ettema & Lorås, 2009. Efficiency in Cycling. A Review. https://www.researchgate.net/publication/24027428_Efficiency_in_cycling_A_review. A logarithmic model predicts GE as a function of PO, with 90% prediction intervals equivalent to ± 2.5% around the marginal estimate. e.g. at 250 W estimated GE = 21%, 90% PI = [18.5, 23.5].

Post 3

GE prediction model derived from data in Ettema & Lorås, 2009 (link in 📊Alt text)

Most of the variance in GE comes from differences in cadence. Most of the rest comes from random cross-sectional individual differences

GE is probably trainable, but veeery slowly

Figure reproduced from Ettema & Lorås, 2009. Efficiency in Cycling. A Review. https://www.researchgate.net/publication/24027428_Efficiency_in_cycling_A_review. Figure description reads: Fig. 2 Overview of literature data explored in this review and used in the quantification of efficiency… d Same data as in b, but depicting a possible error of measurement of 5%. Thick curve is the average curve, based on the regression line in b. Thin curves indicate ranges if both metabolic rate and external power have deviation (error) of 5%, but in opposite directions. A thick vertical error bar indicates the same range if only one of the measures has a 5% deviation; the thin horizontal arrows indicate the efficiency difference following from this error.

Post 4

Typical reported range for fractional threshold (%VO2max @ CP, FTP, MLSS, VT2, LT2, etc) in trained cyclists is anywhere from 75-90%+, possibly higher in elite and female athletes

I can’t find a good dataset for cyclists right now, so here is one for treadmill running (link & details in 📊Alt text)

Figure & table from Benítez-Muñoz et al, 2024. Differences in the ventilatory thresholds in treadmill according to training status in 971 males and 301 females: a cross-sectional study. https://pmc.ncbi.nlm.nih.gov/articles/PMC11829848/. Showing percent VO2max at ventilatory threshold 2 (close equivalence to critical power or functional threshold power) across a range of fitness levels in runners, from 85-92%. Running threshold will tend to be slightly higher %VO2max compared to cycling threshold, hence the low-end fitness estimate is higher than typically observed in cyclists. But the high-end estimate is probably similarly bounded around 90-92%.

Post 5

RER is a function of exercise intensity and substrate (lipid/glucose) oxidation

At threshold, RER will be ~0.85-0.95, maybe up to 1.00 by the end.

Along each GE isoline in the top figure, RER equates to a small difference of 2 %VO2max. GE drives the big differences

Post 6

So for this specific question “is it possible to achieve an FTP of 380 W at 5.0 L/min VO2max?”

The answer is yes, with elite-level metabolic efficiency ~24% and fractional threshold ~90%VO2max

*How* to achieve that is an entirely different question which I don’t have code for (yet 😉)